/**
  ******************************************************************************
  * @file    TCP_OpenServer.c
  * @author  exatron
  * @version V1.0.0
  * @date    segunda, 22 de dezembro de 2014
  * @brief   Função para abrir um socket server.
  ******************************************************************************
  * @attention
  *
  *
  ******************************************************************************
  */

/** @addtogroup TCP
  * @{
  */

/* Includes ------------------------------------------------------------------*/
#include <common.h>

/* Private variables ---------------------------------------------------------*/
extern Socket_t sock[N_SOCKS];

extern FIFO_t isrx_fifo;

extern BooleanType_t ISRINGS;
extern BooleanType_t ISCLOSE;

/* External functions --------------------------------------------------------*/

/** @defgroup TCP_Functions
  * @{
  */

/**
  * @brief  Função para abrir um socket server.
  * @param  SocketID_t: ID do socket server
  * @param  int porta: Porta a escutar
  * @retval Devolve pela serial Result_t
  */
void TCP_OpenServer(int porta)
{
	int i=0;
	int yes=1;

	/* Fecha instância anterior */
	if(aux_CloseSocket(TCP_ServerSocketID) == Result_Ok)
	{
		aux_USART0_puts("%s=%d;%d\r\n", "SCK_CLOS", TCP_ServerSocketID, Result_Ok);
	}

    /* Verifica se o socket esta aberto */
    if(sock[TCP_ServerSocketID].status != Status_SocketClosed)
    {
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_AlreadyExists);
		aux_SetBusy(FALSE);
		return;
    }

    /* Verifica se a porta ja esta aberta */
    i=0;
    while(i<(N_SOCKS))
    {
    	if(sock[i].porta == porta)
    	{
			aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_PortOpened);
			aux_SetBusy(FALSE);
			return;
    	}
    	i++;
    }

    /* Verifica se possui timeout válido */
    if(aux_CFG_GetTimeOutSocket() == -1)
    {
    	aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_Error);
    	aux_SetBusy(FALSE);
    	return;
    }

	/* Verifica se esta em rede */
	if(aux_WF_GetIP() != Result_Ok)
	{
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_NoConnection);
		aux_SetBusy(FALSE);
		return;
	}

    /* Zera estrutura do socket */
    aux_ClearSocket(TCP_ServerSocketID);

    /* Abre o socket */
    sock[TCP_ServerSocketID].serfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock[TCP_ServerSocketID].serfd < 0)
    {
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_OnOpen);
		aux_SetBusy(FALSE);
		return;
    }

    /* Seta id e informações de endereço */
    sock[TCP_ServerSocketID].ID = TCP_ServerSocketID;
    sock[TCP_ServerSocketID].seraddr.sin_port = htons(porta);
    sock[TCP_ServerSocketID].seraddr.sin_family = AF_INET;
    sock[TCP_ServerSocketID].seraddr.sin_addr.s_addr = INADDR_ANY;	// Pŕoprio IP

    /* Reutiliza a porta */
    if(setsockopt(sock[TCP_ServerSocketID].serfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
    {
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_OnSet);
		aux_SetBusy(FALSE);
		return;
    }

    if(bind(sock[TCP_ServerSocketID].serfd,(struct sockaddr *)&sock[TCP_ServerSocketID].seraddr,sizeof(sock[TCP_ServerSocketID].seraddr))<0)
    {
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_OnBinding);
		aux_SetBusy(FALSE);
		return;
    }

    /* Houve uma conexão */
    if(listen(sock[TCP_ServerSocketID].serfd,0)<0)
    {
		aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_OnListen);
		aux_SetBusy(FALSE);
		return;
    }

    /* Escreve a porta */
    sock[TCP_ServerSocketID].porta = porta;

    /* Escreve o tipo de socket */
    sock[TCP_ServerSocketID].type = TCP_SERVER;

    /* Seta status do socket */
	sock[TCP_ServerSocketID].status = Status_SocketListen;

    /* Informa Abertura do Socket */
    aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", TCP_ServerSocketID, Result_Ok);

	aux_SetBusy(FALSE);
}

void TCP_OpenServer_accept(SocketID_t id)
{
	int i;
	Socket_t temp_sock;
	socklen_t clilen;
	clilen = sizeof(sock[0].cliaddr);

	id = 0;

	memset(&temp_sock, 0, sizeof(temp_sock));
	temp_sock.clifd = accept(sock[TCP_ServerSocketID].serfd,(struct sockaddr *)&temp_sock.cliaddr,&clilen);

	/* Verifica se há algum socket disponível */
	for(i=Sock0;i<(N_BACKLOG+Sock0);i++)
	{
		if(sock[i].status == Status_SocketClosed)
		{
			id = i;
			break;
		}
		/* Se não há nenhum aceita e fecha */
		else if(i == (N_BACKLOG+Sock0-1))
		{
			if(temp_sock.clifd > 0)
				close(temp_sock.clifd);
		}
	}

	/* Se havia socket livre para receber conexão */
	if(id > 0)
	{
		/* Limpa estrutura do socket */
	    aux_ClearSocket(id);

		memcpy(&sock[id].clifd, &temp_sock.clifd, sizeof(sock[id].clifd));
		memcpy(&sock[id].cliaddr, &temp_sock.cliaddr, sizeof(sock[id].cliaddr));

		if(sock[id].clifd < 0)
		{
			aux_USART0_puts("%s=%d;%d\r\n", "TCP_OPS", id, Result_OnAccept);

			/* Limpa estrutura do socket */
			aux_ClearSocket(id);
		}
		else
		{
			/* Seta id */
			sock[id].ID = id;
			/* Seta status do socket */
			sock[id].status = Status_SocketConnected;
			/* Escreve o tipo de socket */
			sock[id].type = TCP_CLIENT;
			/* Informa Conexão */
			if(ISRINGS == TRUE)
			{
				aux_USART0_puts("%s=%d;%d\r\n", "SCK_GSS", id, Status_SocketConnected);
			}
		}
	}
}

void TCP_OpenServer_recv(SocketID_t id)
{
	char buf_temp[RX_BUF_LEN];

	bzero(buf_temp, sizeof(buf_temp));

	/* Se descritor válido */
	if(sock[id].clifd > 0)
		sock[id].nb_rec = read(sock[id].clifd, buf_temp, RX_BUF_LEN-1);
	else
		return;

	/* Se dados válidos */
	if(sock[id].nb_rec > 0)
	{
		sock[id].istimeout = 0;

		fifo_write(&sock[id].fifo_rx, buf_temp, sock[id].nb_rec);

		if(sock[id].isrx == NoRead)
		{
			sock[id].isrx = IsRead;

			fifo_write(&isrx_fifo, &id, 1);

			if(ISRINGS == TRUE)
			{
				aux_SendSignal("SCK_IRDR=%d;%d\r\n", sock[id].ID, Result_Ok);
			}
		}
	}
	/* Erro no recebimento */
	else
	{
		/* Fecha sock */
		if(sock[id].clifd > 0)
			close(sock[id].clifd);

		/* Informa Desconexão */
		if(ISRINGS == TRUE || ISCLOSE == TRUE)
		{
			aux_USART0_puts("%s=%d;%d\r\n", "SCK_CLOS", id, Result_Ok);
			ISCLOSE = FALSE;
		}

		/* Limpa estrutura do socket */
		aux_ClearSocket(id);
	}
}

/**
  * @}
  */

/**
  * @}
  */







